#include "WorkStealingContainer.h"

__thread int WorkStealingContainer::queueNum = -1;

WorkStealingContainer::WorkStealingContainer(int maxConsumerNum){
	this->maxConsumerNum = maxConsumerNum;
	this->queues = new MSQueue<Task*>*[maxConsumerNum];
	for(int i = 0; i < maxConsumerNum; i++)
	{
		queues[i] = NULL;
	}
	this->consumerCount = 0;
	this->containerSize = 0;
	
	/****** debug info ***********/
	/*
	this->taskCounters = new int[maxConsumerNum];   // number of tasks inserted to each queue
	for(int i = 0; i < maxConsumerNum; i++)
	{
		taskCounters[i] = 0;
	}
	this->consumerDistribution = new int*[maxConsumerNum];   // distribution of successful get operations between the queues
	for(int i = 0; i < maxConsumerNum; i++)
	{
		consumerDistribution[i] = new int[maxConsumerNum];
		for(int j = 0; j < maxConsumerNum; j++)
		{
			consumerDistribution[i][j] = 0;
		}
	}
	*/
	/******************************/
}


void WorkStealingContainer::registerConsumer(){
	queueNum = __sync_fetch_and_add(&consumerCount,1);
	queues[queueNum] = new MSQueue<Task*>();
	__sync_add_and_fetch(&containerSize,1);
}

void WorkStealingContainer::registerProducer(int tid){
	srand(TaskContainer::generateRandomSeed());
}

void WorkStealingContainer::put(Task* task, Statistics* stat){
	if(containerSize == 0)	// no consumer queues available
	{
		delete task;   // LOST TASKS!!
		return;
	}
	
	int index = rand() % consumerCount;
	while(queues[index] == NULL)
	{
		index = (index + 1)%consumerCount;
	}
	queues[index]->enqueue(task,stat);
	//__sync_fetch_and_add(&taskCounters[index],1);  //for debug only
}

Task* WorkStealingContainer::get(Statistics* stat){
	if(containerSize == 0)   // container not initialized
	{
		return NULL;
	}
	if(queueNum < 0)   // unregistered consumer - picks a random queue (only at first insertion
	{
		srand(TaskContainer::generateRandomSeed());
		queueNum = rand()%containerSize;
	}
	
	Task* task;
	// consumer first tries to retrieve from its own queue
	if(queues[queueNum] != NULL && queues[queueNum]->dequeue(task,stat))              
	{	
		//consumerDistribution[queueNum][queueNum]++;  // debug
		return task;
	}
	
	// if its queue was empty - the consumer traverses the other queues
	for(int i = 1; i < containerSize; i++)
	{
		int index = (queueNum + i)%containerSize;
		if(queues[index] != NULL && queues[index]->dequeue(task,stat))  
		{		
			//consumerDistribution[queueNum][index]++;  // debug
			return task;
		}
	}
	return NULL;
}


WorkStealingContainer::~WorkStealingContainer(){
	/******* print debug info **********************/
	/*
	cout << "task distribution between queues:" << endl;
	for(int i = 0; i < maxConsumerNum; i++)
	{
		cout << taskCounters[i] << " ";
	}
	cout << endl;
	delete[] taskCounters;
	cout << "consumer distribution between queues:" << endl;
	for(int i = 0; i < maxConsumerNum; i++)
	{
		for(int j = 0; j < maxConsumerNum; j++)
		{
			cout << consumerDistribution[i][j] << " ";
		}
		cout << endl;
		cout << endl;
		delete[] consumerDistribution[i];
	}
	delete[] consumerDistribution;
	*/
	/***********************************/
	Statistics* dummy_stat = new Statistics();
	for(int i = 0; i < maxConsumerNum; i++)
	{
		Task* t;
		while(queues[i] != NULL && queues[i]->dequeue(t,dummy_stat))
		{
			delete t;
		}
		if(queues[i] != NULL)
		{
			delete queues[i];
		}
	}
	delete dummy_stat;
	delete queues;
}